package tomasulogui;

import tomasulogui.IssuedInst.INST_TYPE;

public class ROBEntry {

    ReorderBuffer rob;
    // TODO - add many more fields into entry
    // I deleted most, and only kept those necessary to compile GUI
    boolean busy = false;         //
    boolean complete = false;
    boolean predictTaken = false;
    boolean mispredicted = false;
    int branchTgt = -1;
    int instPC = -1;
    int writeReg = -1;
    int writeValue = -1;
    int immediate = -1;           //
    int reg1Tag = -1;             //
    int reg1Value = 0;            //
    boolean reg1Ready = false;    //
    int reg2Tag = -1;             //
    int reg2Value = 0;            // also used for immediates
    boolean reg2Ready = false;    //
    boolean hasWritten = false;
    IssuedInst.INST_TYPE opcode;

    public int getReg1Tag() {
        return reg1Tag;
    }

    public void setReg1Tag(int reg1Tag) {
        this.reg1Tag = reg1Tag;
    }

    public int getReg1Value() {
        return reg1Value;
    }

    public void setReg1Value(int reg1Value) {
        this.reg1Value = reg1Value;
        this.reg1Ready = true;
    }

    public boolean isReg1Ready() {
        return reg1Ready;
    }

    public int getReg2Tag() {
        return reg2Tag;
    }

    public void setReg2Tag(int reg2Tag) {
        this.reg2Tag = reg2Tag;
    }

    public int getReg2Value() {
        return reg2Value;
    }

    public void setReg2Value(int reg2Value) {
        this.reg2Value = reg2Value;
        this.reg2Ready = true;
    }

    public boolean isReg2Ready() {
        return reg2Ready;
    }

    public ROBEntry(ReorderBuffer buffer) {
        rob = buffer;
    }

    public boolean isBusy() {
        return busy;
    }

    public boolean isComplete() {
        return complete;
    }

    public boolean branchMispredicted() {
        return mispredicted;
    }

    public boolean getPredictTaken() {
        return predictTaken;
    }

    public int getInstPC() {
        return instPC;
    }

    public IssuedInst.INST_TYPE getOpcode() {
        return opcode;
    }

    public void setOpcode(IssuedInst.INST_TYPE type) {
        opcode = type;
        switch (type) {
            //instructions with one register
            case ADDI:
            case ANDI:
            case ORI:
            case XORI:
            case LOAD:
            case STORE:
            case BLTZ:
            case BGEZ:
            case BGTZ:
            case JR:
            case JALR:
            case SLL:
            case SRL:
            case SRA:
                reg2Ready = true;
                break;
            //instructions with two registers
            case ADD:
            case SUB:
            case MUL:
            case DIV:
            case AND:
            case OR:
            case XOR:
            case BEQ:
            case BNE:
                break;
            //instructions with no registers
            case NOP:
            case J:
            case JAL:
                reg1Ready = true;
                reg2Ready = true;
        }
    }

    public boolean isHaltOpcode() {
        return (opcode == IssuedInst.INST_TYPE.HALT);
    }

    public void setBranchTaken(boolean result) {
        // TODO - maybe more than simple set
        if (result == predictTaken) {
            mispredicted = false;
        } else {
            mispredicted = true;
        }
        complete = true;
    }

    public int getBranchTgt() {
        return branchTgt;
    }

    public void setBranchTgt(int branchTgt) {
        this.branchTgt = branchTgt;
    }

    public int getWriteReg() {
        return writeReg;
    }

    public int getWriteValue() {
        return writeValue;
    }

    public void setWriteValue(int value) {
        writeValue = value;
    }

    public void copyInstData(IssuedInst inst, int frontQ) {

        if (inst.opcode == INST_TYPE.STORE) {
            if (inst.regSrc2Used) {
                int tag = rob.findDestReg(inst.getRegSrc2());
                if (tag == -1) {
                    inst.setRegSrc2Value(rob.regs.regs[inst.regSrc2]);
                    setReg2Value(rob.regs.regs[inst.regSrc2]);
                    inst.setRegSrc2Valid();
                } else {
                    if(rob.buff[tag].hasWritten){
                        inst.setRegSrc2Valid();
                        inst.setRegSrc2Value(rob.buff[tag].getWriteValue());
                        setReg2Value(rob.buff[tag].getWriteValue());
                    }
                    else{
                        inst.setRegSrc2Tag(tag);
                        setReg2Tag(tag);
                    }
                }
            }

            if (inst.regSrc1Used) {
                int tag = rob.findDestReg(inst.getRegSrc1());
                if (tag == -1) {
                    inst.setRegSrc1Value(rob.regs.regs[inst.regSrc1]);
                    setReg1Value(rob.regs.regs[inst.regSrc1]);
                    inst.setRegSrc1Valid();
                } else {
                    if(rob.buff[tag].hasWritten){
                        inst.setRegSrc1Valid();
                        inst.setRegSrc1Value(rob.buff[tag].getWriteValue());
                        setReg1Value(rob.buff[tag].getWriteValue());
                    }
                    inst.setRegSrc1Tag(tag);
                    setReg1Tag(tag);
                }
            }

            if (inst.getRegSrc1Valid() && inst.getRegSrc2Valid()) {
                writeValue = inst.getRegSrc1Value() + inst.getImmediate();
                complete = true;
                busy = false;
            }
        } else {
            if (inst.regSrc1Used) {
                int tag = rob.findDestReg(inst.getRegSrc1());
                if (tag == -1) {
                    inst.setRegSrc1Value(rob.regs.regs[inst.regSrc1]);
                    setReg1Value(rob.regs.regs[inst.regSrc1]);
                    inst.setRegSrc1Valid();
                } else {
                    if(rob.buff[tag].hasWritten){
                        inst.setRegSrc1Valid();
                        inst.setRegSrc1Value(rob.buff[tag].getWriteValue());
                        setReg1Value(rob.buff[tag].getWriteValue());
                    }
                    inst.setRegSrc1Tag(tag);
                    setReg1Tag(tag);
                }
            }

            if (inst.regSrc2Used) {
                int tag = rob.findDestReg(inst.getRegSrc2());
                if (tag == -1) {
                    inst.setRegSrc2Value(rob.regs.regs[inst.regSrc2]);
                    setReg2Value(rob.regs.regs[inst.regSrc2]);
                    inst.setRegSrc2Valid();
                } else {
                    if(rob.buff[tag].hasWritten){
                        inst.setRegSrc2Valid();
                        inst.setRegSrc2Value(rob.buff[tag].getWriteValue());
                        setReg2Value(rob.buff[tag].getWriteValue());
                    }
                    inst.setRegSrc2Tag(tag);
                    setReg2Tag(tag);
                }
            }
        }

        inst.setRegDestTag(frontQ);
        if (inst.getRegDest() > 0) {
            rob.setTagForReg(inst.getRegDest(), frontQ);
        }



        instPC = inst.getPC();
        setOpcode(inst.getOpcode());
        writeReg = inst.getRegDest();
        immediate = inst.getImmediate();

        if (inst.determineIfBranch()) {
            rob.simulator.getBTB().predictBranch(inst);
            predictTaken = inst.branchPrediction;
            
            if(inst.getOpcode() == INST_TYPE.JAL || inst.getOpcode() == INST_TYPE.JALR){
                writeReg = 31;
            }
            if(inst.getOpcode() == INST_TYPE.JR || inst.getOpcode() == INST_TYPE.JALR){
                inst.branchPrediction = false;
                predictTaken = false;
            }
        }




        //set values for regSrc1 and 2
        //      -check for immediates
        //      -load from reg if tag is -1
        //set valid bits for regSrc1 and 2?


        // TODO - This is a long and complicated method, probably the most complex
        // of the project.  It does 2 things:
        // 1. update the instruction, as shown in 2nd line of code above
        // 2. update the fields of the ROBEntry, as shown in the 1st line of code above

    }
}
